home *** CD-ROM | disk | FTP | other *** search
- /*-
- * Copyright (c) 1995 Leonard Norrgard. All rights reserved.
- * Copyright (c) 1994 Christopher G. Demetriou. All rights reserved.
- * Copyright (c) 1982, 1986, 1989, 1993
- * The Regents of the University of California. All rights reserved.
- * (c) UNIX System Laboratories, Inc.
- * All or some portions of this file are derived from material licensed
- * to the University of California by American Telephone and Telegraph
- * Co. or Unix System Laboratories, Inc. and are reproduced herein with
- * the permission of UNIX System Laboratories, Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * from: @(#)sys_process.c 8.1 (Berkeley) 6/10/93
- */
-
- /*
- * References:
- * (1) Bach's "The Design of the UNIX Operating System",
- * (2) sys/miscfs/procfs from UCB's 4.4BSD-Lite distribution,
- * (3) the "4.4BSD Programmer's Reference Manual" published
- * by USENIX and O'Reilly & Associates.
- * The 4.4BSD PRM does a reasonably good job of documenting what the various
- * ptrace() requests should actually do, and its text is quoted several times
- * in this file.
- */
-
- #define _KERNEL
- #include "ixemul.h"
- #include "kprintf.h"
-
- #include <signal.h>
- #include <string.h>
- #include <sys/ptrace.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <exec/execbase.h>
-
- int process_read_regs (struct user *p, struct reg *regs)
- {
- if (p->u_regs == NULL)
- {
- bzero(regs, sizeof(struct reg));
- errno = EIO;
- return -1;
- }
- bcopy (p->u_regs, regs, sizeof (struct reg));
- return 0;
- }
-
- int process_write_regs (struct user *p, struct reg *regs)
- {
- if (p->u_regs == NULL)
- {
- errno = EIO;
- return -1;
- }
- bcopy (regs, p->u_regs, sizeof (struct reg));
- return 0;
- }
-
- int process_read_fpregs (struct user *p, struct fpreg *fpregs)
- {
- if (p->u_fpregs == NULL)
- {
- bzero(fpregs, sizeof(struct fpreg));
- errno = EIO;
- return -1;
- }
- bcopy (p->u_fpregs, fpregs, sizeof (struct fpreg));
- return 0;
- }
-
- int process_write_fpregs (struct user *p, struct fpreg *fpregs)
- {
- if (p->u_fpregs == NULL)
- {
- errno = EIO;
- return -1;
- }
- bcopy (fpregs, p->u_fpregs, sizeof (struct fpreg));
- return 0;
- }
-
- int process_sstep (struct user *t, int sstep)
- {
- if (sstep)
- if (t->u_regs == NULL)
- {
- errno = EIO;
- return -1;
- }
- else
- t->u_regs->r_sr |= 0x8000;
- else if (t->u_regs)
- t->u_regs->r_sr &= ~0x8000;
- return 0;
- }
-
- int process_set_pc (struct user *t, caddr_t addr)
- {
- if (t->u_regs)
- {
- t->u_regs->r_pc = addr;
- return 0;
- }
- errno = EIO;
- return -1;
- }
-
- int ptrace (int request, pid_t pid, caddr_t addr, int data)
- {
- struct Task *task, *me = FindTask(0);
- struct user *t, *p;
- int step;
- int error;
-
- /* Find the user area of this process. */
- p = me->tc_TrapData;
-
- if (request == PT_GETIXINFO)
- {
- struct small_ixnet_base {
- struct Library ixnet_lib;
- unsigned char ix_myflags;
- unsigned char ix_pad;
- BPTR ix_seg_list;
- };
-
- static struct ixinfo info;
- extern void sig_trampoline();
- extern void sig_launch();
- extern void install_vector(); /* trap.s */
- extern void restore_vector(); /* trap.s */
-
- info.version = 0;
- info.ixemul_seglist = ix.ix_seg_list;
- info.ixnet_seglist = (u.u_ixnetbase ? ((struct small_ixnet_base *)(u.u_ixnetbase))->ix_seg_list : NULL);
- info.sigtramp_start = (long)sig_trampoline;
- info.sigtramp_end = (long)sig_launch;
- if (betterthan68010())
- {
- info.install_vector = install_vector;
- info.restore_vector = restore_vector;
- }
- else
- {
- info.install_vector = NULL;
- info.restore_vector = NULL;
- }
- return (int)&info;
- }
- else if (request == PT_TRACE_ME)
- task = me;
- else if ((request == PT_ATTACH))
- {
- /* have to check if the task really exists */
- if (pid == 0 || (task = pfind(pid)) == NULL)
- {
- errno = ESRCH;
- return -1;
- }
- }
- else
- task = (struct Task *) pid;
-
- /* Temporarily until I'm convinced it all works. It makes it also easier
- to debug gdb. */
- #if 0
- {
- char *req;
-
- switch (request)
- {
- case PT_TRACE_ME: req = "PT_TRACE_ME"; break;
- case PT_READ_I: req = "PT_READ_I"; break;
- case PT_READ_D: req = "PT_READ_D"; break;
- case PT_READ_U: req = "PT_READ_U"; break;
- case PT_WRITE_I: req = "PT_WRITE_I"; break;
- case PT_WRITE_D: req = "PT_WRITE_D"; break;
- case PT_WRITE_U: req = "PT_WRITE_U"; break;
- case PT_CONTINUE: req = "PT_CONTINUE"; break;
- case PT_KILL: req = "PT_KILL"; break;
- case PT_STEP: req = "PT_STEP"; break;
- case PT_GETSEGS: req = "PT_GETSEGS"; break;
- case PT_GETIXINFO: req = "PT_GETIXINFO"; break;
- case PT_GETREGS: req = "PT_GETREGS"; break;
- case PT_SETREGS: req = "PT_SETREGS"; break;
- case PT_GETEXENAME: req = "PT_GETEXENAME";break;
- case PT_GETA4: req = "PT_GETA4"; break;
- case PT_GETFPREGS: req = "PT_GETFPREGS"; break;
- case PT_SETFPREGS: req = "PT_SETFPREGS"; break;
- case PT_ATTACH: req = "PT_ATTACH"; break;
- case PT_DETACH: req = "PT_DETACH"; break;
- default: req = "*Unknown request*"; break;
- }
- switch (request)
- {
- case PT_READ_I: break;
- case PT_READ_D: break;
- case PT_READ_U: break;
- case PT_WRITE_I: break;
- case PT_WRITE_D: break;
- case PT_WRITE_U: break;
- default:break;
-
- case PT_KILL:
- case PT_CONTINUE:
- case PT_STEP:
- KPrintF("ptrace (%s, pid=%lx, addr=%lx , data=%lx);\n",
- req, pid, addr, data);
- }
- }
- #endif
-
- /* sanity check */
- if (task == NULL || (t = (struct user *) task->tc_TrapData) == NULL)
- {
- errno = ESRCH;
- return -1;
- }
-
- /* Check that the arguments are valid. */
- switch (request)
- {
- case PT_TRACE_ME:
- /* Saying that you're being traced is always OK. */
- break;
-
- case PT_ATTACH:
- /* You can't attach to a process if:
- (1) it's the process that's doing the attaching or */
- if (t == FindTask (0)->tc_TrapData)
- {
- errno = EPERM;
- return -1;
- }
-
- /* (2) it's already being traced. */
- if (t->p_flag & STRC)
- {
- errno = EPERM;
- return -1;
- }
- break;
-
- case PT_READ_I:
- case PT_READ_D:
- case PT_WRITE_I:
- case PT_WRITE_D:
- case PT_CONTINUE:
- case PT_KILL:
- case PT_DETACH:
- case PT_GETFPREGS:
- case PT_SETFPREGS:
-
- /* You can't do what you want to the process if: */
-
- /* (1) It's not being traced at all, */
- if (!(t->p_flag & STRC))
- {
- errno = EPERM;
- return -1;
- }
-
- /* (2) it's not being traced by _you_, or */
- if (t->p_pptr->pr_Task.tc_TrapData != p)
- {
- errno = EPERM;
- return -1;
- }
-
- /* (3) it's not currently stopped. */
- if (t->p_stat != SSTOP
- || !(t->p_flag & SWTED))
- {
- errno = EPERM;
- return -1;
- }
- break;
-
- case PT_STEP:
- case PT_GETREGS:
- case PT_SETREGS:
- case PT_GETSEGS: /* you can always do this */
- case PT_GETEXENAME: /* you can always do this */
- case PT_GETA4: /* you can always do this */
- break;
-
- default:
- /* It was not a valid request. */
- errno = EIO;
- return -1;
- }
-
- /* Now actually do the job. */
- step = 0;
-
- switch (request)
- {
- case PT_TRACE_ME:
- /* Child declares it's being traced, just set the trace flag. */
- t->p_flag |= STRC;
- break;
-
- case PT_READ_I:
- case PT_READ_D:
- /* Check whether this is valid memory */
- if (((int)addr & 1) || addr == 0 || ((TypeOfMem(addr)) == 0))
- {
- errno = EIO;
- return -1;
- }
- return *((int *)addr);
-
- case PT_WRITE_I:
- case PT_WRITE_D:
- /* Check whether this is valid memory */
- if (((int)addr & 1) || addr == 0 || ((TypeOfMem(addr)) == 0))
- {
- errno = EIO;
- return -1;
- }
- *((int *)addr) = data;
- CacheClearE(addr, 4, CACRF_ClearI | CACRF_ClearD);
- return 0;
-
- case PT_GETSEGS:
- return (t->u_segs ? (int)t->u_segs->segment : 0);
-
- case PT_GETEXENAME:
- return (t->u_segs ? (int)t->u_segs->name : 0);
-
- case PT_GETA4:
- return t->u_a4;
-
- /* case PT_READ_U: fixme */
- /* case PT_WRITE_U: fixme */
-
- case PT_STEP:
- /* From the 4.4BSD PRM:
- "Execution continues as in request PT_CONTINUE; however
- as soon as possible after execution of at least one
- instruction, execution stops again. [ ... ]" */
- step = 1;
- /* fallthrough */
-
- case PT_CONTINUE:
- /* From the 4.4BSD PRM:
- "The data argument is taken as a signal number and the
- child's execution continues at location addr as if it
- incurred that signal. Normally the signal number will
- be either 0 to indicate that the signal that caused the
- stop should be ignored, or that value fetched out of
- the process's image indicating which signal caused
- the stop. If addr is (int *)1 then execution continues
- from where it stopped." */
- /* step = 0 done above. */
-
- /* Check that data is a valid signal number or zero. */
- if (data < 0 || data >= NSIG)
- {
- errno = EIO;
- return -1;
- }
-
- /* Arrange for a single-step, if that's requested and possible. */
- if ((error = process_sstep (t, step)))
- return error;
-
- /* If the address parameter is not (int *)1, set the pc. */
- if ((int *)addr != (int *)1)
- if ((error = process_set_pc (t, addr)))
- return error;
-
- /* Finally, deliver the requested signal (or none). */
- sendsig:
- t->p_xstat = data;
- setrun (task);
- return 0;
-
- case PT_KILL:
- /* not being traced any more */
- t->p_flag &= ~STRC;
- /* Just send the process a KILL signal. */
- data = SIGKILL;
- goto sendsig;
-
- case PT_GETREGS:
- return process_read_regs (t, (struct reg *)addr);
-
- case PT_SETREGS:
- return process_write_regs (t, (struct reg *)addr);
-
- case PT_GETFPREGS:
- return process_read_fpregs (t, (struct fpreg *)addr);
-
- case PT_SETFPREGS:
- return process_write_fpregs (t, (struct fpreg *)addr);
-
- case PT_ATTACH:
- /*
- * Go ahead and set the trace flag.
- * Save the old parent (it's reset in
- * _DETACH, and also in vfork.c:wait4()
- * Reparent the process so that the tracing
- * proc gets to see all the action.
- * Stop the target.
- */
- t->p_flag |= STRC;
- t->p_xstat = 0; /* XXX ? */
- if (t->p_pptr != (struct Process *)me) {
- t->p_opptr = t->p_pptr;
- proc_reparent((struct Process *)task, (struct Process *)me);
- }
- _psignal(task, SIGSTOP);
- return (0);
-
- case PT_DETACH:
- /* not being traced any more */
- t->p_flag &= ~STRC;
-
- /* give process back to original parent */
- if (t->p_opptr != t->p_pptr)
- {
- if (t->p_opptr && pfind((pid_t)t->p_opptr))
- proc_reparent((struct Process *)task, t->p_opptr);
- }
-
- t->p_opptr = NULL;
- t->p_flag &= ~SWTED;
-
- /* and deliver any signal requested by tracer. */
- if (t->p_stat == SSTOP)
- goto sendsig;
- else if (data)
- _psignal(task, data);
-
- return (0);
-
- default:
- /* Unknown request. */
- errno = EIO;
- return -1;
- }
- return 0; /* correct return value? */
- }
-